package com.dokicam.colordepth;

import android.app.Activity;
import android.app.AlertDialog;
import android.content.DialogInterface;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.support.annotation.Nullable;
import android.util.Log;
import android.view.Surface;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import android.view.View;
import android.widget.TextView;
import android.widget.Toast;

import com.ftdi.j2xx.D2xxManager;
import com.ftdi.j2xx.FT_Device;
import com.hjimi.api.iminect.ImiDevice;
import com.hjimi.api.iminect.ImiDeviceAttribute;
import com.hjimi.api.iminect.ImiDeviceState;
import com.hjimi.api.iminect.ImiFrameMode;
import com.hjimi.api.iminect.ImiFrameType;
import com.hjimi.api.iminect.ImiNect;
import com.hjimi.api.iminect.ImiPixelFormat;
import com.scanLab.ScanResult;

import java.util.IllegalFormatConversionException;


public class MainActivity extends Activity implements SimpleViewer.ScanCallBack{

    private static final String TAG = "MainActivity";

    // j2xx
    public static D2xxManager ftD2xx = null;
    private FT_Device ftDev;

    private TextView tv_show;
    
    private SurfaceView mColorView;
    private GLPanel mGLColorPanel;
    private GLPanel mGLDepthPanel;
    private DecodePanel mDecodePanel;
    private Surface mSurface;

    private ImiNect m_ImiNect = null;
    private ImiDevice mDevice;
    private SimpleViewer mColorViewer;
    private SimpleViewer mDepthViewer;
    private ImiFrameMode mCurrColorMode;
    private ImiFrameMode mCurrDepthMode;
    private ImiDeviceAttribute mDeviceAttribute = null;

    private static final int DEVICE_OPEN_SUCCESS = 0;
    private static final int DEVICE_OPEN_FALIED = 1;
    private static final int DEVICE_DISCONNECT = 2;

    private int expectDepthFrameWidth = 640;
    private int expectDepthFrameHeight = 480;

    private ImiDeviceState deviceState = ImiDeviceState.IMI_DEVICE_STATE_CONNECT;

    final byte XON = 0x11;    /* Resume transmission */
    final byte XOFF = 0x13;    /* Pause transmission */


    byte[] writeBuffer;

    int baudRate = 115200; /* baud rate */
    byte stopBit = 1; /* 1:1stop bits, 2:2 stop bits */
    byte dataBit = 8; /* 8:8bit, 7: 7bit */
    byte parity = 0; /* 0: none, 1: odd, 2: even, 3: mark, 4: space */
    byte flowControl = 0; /* 0:none, 1: CTS/RTS, 2:DTR/DSR, 3:XOFF/XON */


    private Handler MainHandler = new Handler() {
        @Override
        public void handleMessage(Message msg) {
            super.handleMessage(msg);
            switch (msg.what) {
                case DEVICE_OPEN_FALIED:
                case DEVICE_DISCONNECT:
                    showMessageDialog();
                    break;
                case DEVICE_OPEN_SUCCESS:
                    runViewer();
                    break;
            }
        }
    };

    private void showMessageDialog() {
        AlertDialog.Builder builder = new AlertDialog.Builder(this);
        builder.setTitle("The device is not connected!!!");
        builder.setPositiveButton("quit", new DialogInterface.OnClickListener() {
            @Override
            public void onClick(DialogInterface dialog, int arg1) {
                dialog.dismiss();
                finish();
            }
        });
        builder.show();
    }

    private void runViewer() {
        mColorViewer = new SimpleViewer(mDevice, ImiFrameType.COLOR);
        mDepthViewer = new SimpleViewer(mDevice, ImiFrameType.DEPTH);

        switch (mCurrColorMode.getFormat()) {
            case IMI_PIXEL_FORMAT_IMAGE_H264:
                mDecodePanel.initDecoder(mSurface, mCurrColorMode.getResolutionX(),
                        mCurrColorMode.getResolutionY());
                mColorViewer.setDecodePanel(mDecodePanel);
                break;
            default:
                mColorView.setVisibility(View.GONE);
                mGLColorPanel.setVisibility(View.VISIBLE);
                mColorViewer.setGLPanel(mGLColorPanel);
                break;
        }

        mDepthViewer.setGLPanel(mGLDepthPanel);

        mDepthViewer.setCallBack(this);

        mColorViewer.onStart();
        mDepthViewer.onStart();
    }

    private class MainListener implements ImiDevice.OpenDeviceListener,ImiDevice.DeviceStateListener {

        @Override
        public void onOpenDeviceSuccess() {
            //open device success.
            mDevice = m_ImiNect.Device();
            mDeviceAttribute = mDevice.getAttribute();

            //get current color frame mode.
            mCurrColorMode = mDevice.getCurrentFrameMode(ImiFrameType.COLOR);

            //get current depth frame mode.
            mCurrDepthMode = mDevice.getCurrentFrameMode(ImiFrameType.DEPTH);

            try {
                setDepthExpectMode(expectDepthFrameWidth, expectDepthFrameHeight);
            }catch (Exception e){
                Log.e(TAG, "setDepthExpectMode: falied, invalid frame mode");
            }

            MainHandler.sendEmptyMessage(DEVICE_OPEN_SUCCESS);
        }

        @Override
        public void onOpenDeviceFailed(String errorMsg) {
            //open device falied.
            MainHandler.sendEmptyMessage(DEVICE_OPEN_FALIED);
        }

        @Override
        public void onDeviceStateChanged(String deviceUri, ImiDeviceState state) {
            if (state == ImiDeviceState.IMI_DEVICE_STATE_DISCONNECT) {
                //device disconnect.
                //Toast.makeText(MainActivity.this, deviceUri+" DISCONNECT", Toast.LENGTH_SHORT).show();
                if(mDeviceAttribute != null && mDeviceAttribute.getUri().equals(deviceUri)) {
                    deviceState = ImiDeviceState.IMI_DEVICE_STATE_DISCONNECT;
                    MainHandler.sendEmptyMessage(DEVICE_DISCONNECT);
                }
            }else if(state == ImiDeviceState.IMI_DEVICE_STATE_CONNECT){
                //Toast.makeText(MainActivity.this, deviceUri+" CONNECT", Toast.LENGTH_SHORT).show();
                if(mDeviceAttribute != null && mDeviceAttribute.getUri().equals(deviceUri)) {
                    deviceState = ImiDeviceState.IMI_DEVICE_STATE_CONNECT;
                }
            }
        }
    }

    private void setDepthExpectMode(int width, int height) {
        ImiFrameMode frameMode = new ImiFrameMode(ImiPixelFormat.IMI_PIXEL_FORMAT_DEP_16BIT, width, height);
        mDevice.setFrameMode(ImiFrameType.DEPTH, frameMode);
    }

    private class OpenDeviceRunnable implements Runnable {

        @Override
        public void run() {
            //get iminect instance.
            m_ImiNect = ImiNect.create(MainActivity.this);

            MainListener listener = new MainListener();

            m_ImiNect.Device().addDeviceStateListener(listener);

            m_ImiNect.Device().open(listener);
        }
    }

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_color_depth_view);

        mColorView = (SurfaceView) findViewById(R.id.color_view);

        try
        {
            ftD2xx = D2xxManager.getInstance(this);
        }
        catch (D2xxManager.D2xxException e) {
            Log.e(TAG,"getInstance fail!!");}

        tv_show = (TextView) findViewById(R.id.tv_show);

        /* allocate buffer */
        writeBuffer = new byte[512];

        mColorView.getHolder().addCallback(new SurfaceHolder.Callback() {
            @Override
            public void surfaceCreated(SurfaceHolder surfaceHolder) {
                mSurface = surfaceHolder.getSurface();
                mDecodePanel = new DecodePanel();
            }

            @Override
            public void surfaceChanged(SurfaceHolder surfaceHolder, int i, int i1, int i2) {

            }

            @Override
            public void surfaceDestroyed(SurfaceHolder surfaceHolder) {
                mDecodePanel.stopDecoder();
            }
        });

        mGLColorPanel = (GLPanel) findViewById(R.id.sv_color_view);

        mGLDepthPanel = (GLPanel) findViewById(R.id.sv_depth_view);


        new Thread(new OpenDeviceRunnable()).start();


        //连接串口
        connectSerial();

    }


    private void connectSerial(){
        if (ftDev != null && ftDev.isOpen()){
            return;
        }

        int devCount = ftD2xx.createDeviceInfoList(this);
        if (devCount > 0){
            ftDev = ftD2xx.openByIndex(this, 0);

            if (ftDev == null){
                Toast.makeText(this, "open port -1 fail!", Toast.LENGTH_SHORT).show();
                return;
            }

            ftDev.setBitMode((byte)0, D2xxManager.FT_BITMODE_RESET);
            ftDev.setBaudRate(baudRate);
            ftDev.setDataCharacteristics(dataBit, stopBit, parity);
            ftDev.setFlowControl(flowControl, XON, XOFF);
        }
    }



    @Override
    protected void onResume() {
        super.onResume();
        if (mDepthViewer != null) {
            mDepthViewer.onResume();
        }

        if (mColorViewer != null) {
            mColorViewer.onResume();
        }

        connectSerial();
    }

    @Override
    protected void onStop() {
        super.onStop();

        if (ftDev != null && ftDev.isOpen()){
            ftDev.close();
        }

        ftD2xx = null;
        if (mDepthViewer != null) {
            mDepthViewer.onPause();
        }

        if (mColorViewer != null) {
            mColorViewer.onPause();
        }

        if (mDepthViewer != null) {
            mDepthViewer.onDestroy();
        }

        //destroy color viewer.
        if (mColorViewer != null) {
            mColorViewer.onDestroy();
        }

        m_ImiNect.Device().close();

        m_ImiNect.destroy();

        //finish
        finish();

        android.os.Process.killProcess(android.os.Process.myPid());
    }



    @Override
    public void resultCallBack(final ScanResult data) {

        runOnUiThread(new Runnable() {
            @Override
            public void run() {

                try{
                    String width = String.format("%.01f", data.getWidth());

                    String height =  String.format("%.01f", data.getHeight());
                    String length = String.format("%.01f", data.getLength());
                    String show ="length:" + length + "\n"+ "width:" + width + "\n" + "height:" + height + "\n";
                    tv_show.setText(show);
                    String show1 = "length:" + length +  ",width:" + width + "," + "height:" + height;
//                    sendData(show1);

                    combineData((short) (data.getLength() * 10), (short)(data.getWidth()*10), (short)(data.getHeight()*10));

                }catch (IllegalFormatConversionException e){

                }

            }
        });


    }

    short seqnum = 0;
    private void combineData(short length, short width, short height){
        byte[] bytes = new byte[14];

        bytes[0] = 0x02;//stx
        bytes[1] = 9;//lng
        bytes[2] = 0;//lng

        bytes[3] = (byte)seqnum;//seqnum
        bytes[4] = (byte)(seqnum>>>8);//seqnum

        bytes[5] = 0;//cmd

        bytes[6] = (byte)(length);
        bytes[7] = (byte)(length>>>8);//length
        bytes[8] = (byte)(width);
        bytes[9] = (byte)(width>>>8);//width
        bytes[10] = (byte)(height);
        bytes[11] = (byte)(height>>>8);//height

        bytes[12] = 0x03;//etx
        bytes[13] = (byte) (bytes[0]^bytes[1]^bytes[2]^bytes[3]
                ^bytes[4]^bytes[5]^bytes[6]^bytes[7]^bytes[8]
                ^bytes[9]^bytes[10]^bytes[11]^bytes[12]);



        sendData(14, bytes);

        seqnum++;
        if (seqnum > 0x7ffe){
            seqnum = 0;
        }

    }

//    private void sendData(String content){
//        if (ftDev != null && ftDev.isOpen() && content.length() > 0){
//            int numBytes = content.length();
//
//            for (int i = 0; i < numBytes; i++)
//            {
//                writeBuffer[i] = (byte)(content.charAt(i));
//            }
//            sendData(numBytes, writeBuffer);
//        }
//
//    }

    private void sendData(int numBytes, byte[] buffer)
    {
        if (ftDev == null || !ftDev.isOpen()) {
            return;
        }

        if (numBytes > 0)
        {
            ftDev.write(buffer, numBytes);
        }
    }
}
